use std::{error::Error, sync::{atomic::AtomicUsize, Arc}};
use std::fmt::Debug;

use parking_lot::Mutex;

pub trait TaskExecutor<T> where Self:Sized + Send + Sync + 'static {
    type Error:std::error::Error;
    fn exec<S:TaskScheduler<T>>(&mut self, task:T, sender:&S::Sender, task_counter:Arc<AtomicUsize>) -> Result<(), Self::Error>;
    fn run<S:TaskScheduler<T>>(self, recv:S::Receiver, sender:S::Sender, task_counter:Arc<AtomicUsize>) -> Result<(), Self::Error> {
        let mut executor = self;
        while let Some(task) = S::recv(&recv) {
            executor.exec::<S>(task, &sender, task_counter.clone())?;
            task_counter.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
            if task_counter.load(std::sync::atomic::Ordering::SeqCst) == 0 { S::terminal(&sender); }
        }
        Ok(())
    }
}

pub trait TaskScheduler<T> {
    type Sender;
    type Receiver;
    type Error:std::error::Error;
    fn send_with_count(sender:&Self::Sender, task:T, task_counter:&AtomicUsize);
    fn recv(recv:&Self::Receiver) -> Option<T>;
    fn terminal(sender:&Self::Sender);
    fn register_dyn_executor<TE:TaskExecutor<T>>(&mut self, executor:TE);
    fn register_specific_executor<TE:TaskExecutor<T>>(&mut self, name:String, executor:TE) -> bool;
    fn schedule(&mut self) -> Result<(), Self::Error>;
    fn error_collector(&self) -> ErrorCollector<Self::Error>;
}

#[derive(Clone, Debug)]
pub struct ErrorCollector<E:Error> {
    error_vec:Arc<Mutex<Vec<E>>>,
}

impl<E:Error> ErrorCollector<E> {
    pub fn new() -> Self {
        Self { error_vec:Arc::new(Mutex::new(Vec::new())) }
    }

    pub fn collect(&self, result:Result<(), E>) {
        if result.is_ok() { return; }
        let panic_msg = format!("{}", result.as_ref().unwrap_err());
        self.error_vec.lock().push(result.unwrap_err());
        panic!("collect an error:{}", panic_msg);
    }

    pub fn is_error(&self) -> bool {
        !self.error_vec.lock().is_empty()
    }
}